docz-cli 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-K3JRXIEV.js → chunk-26LVQHYL.js} +534 -1
- package/dist/chunk-26LVQHYL.js.map +1 -0
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/{mcp-HXAB75DP.js → mcp-X6AF2C32.js} +202 -3
- package/dist/mcp-X6AF2C32.js.map +1 -0
- package/package.json +8 -3
- package/plugins/nvim/README.md +26 -0
- package/plugins/nvim/lua/docz/init.lua +159 -0
- package/dist/chunk-K3JRXIEV.js.map +0 -1
- package/dist/mcp-HXAB75DP.js.map +0 -1
|
@@ -312,6 +312,301 @@ var DocSyncClient = class {
|
|
|
312
312
|
}
|
|
313
313
|
};
|
|
314
314
|
|
|
315
|
+
// src/collab/text.ts
|
|
316
|
+
import { createHash } from "crypto";
|
|
317
|
+
function collabHash(content) {
|
|
318
|
+
return `sha256:${createHash("sha256").update(content, "utf8").digest("hex")}`;
|
|
319
|
+
}
|
|
320
|
+
var CollabConflictError = class extends Error {
|
|
321
|
+
constructor(currentHash, baseHash) {
|
|
322
|
+
super(
|
|
323
|
+
`collaborative document changed since base_collab_hash (current: ${currentHash}, base: ${baseHash})`
|
|
324
|
+
);
|
|
325
|
+
this.currentHash = currentHash;
|
|
326
|
+
this.baseHash = baseHash;
|
|
327
|
+
this.name = "CollabConflictError";
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
var CollabBaseHashRequiredError = class extends Error {
|
|
331
|
+
constructor(currentHash) {
|
|
332
|
+
super(
|
|
333
|
+
`base_collab_hash is required unless force is explicitly enabled (current: ${currentHash})`
|
|
334
|
+
);
|
|
335
|
+
this.currentHash = currentHash;
|
|
336
|
+
this.name = "CollabBaseHashRequiredError";
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
function getYText(doc) {
|
|
340
|
+
return doc.getText("content");
|
|
341
|
+
}
|
|
342
|
+
function readText(doc) {
|
|
343
|
+
return getYText(doc).toString();
|
|
344
|
+
}
|
|
345
|
+
function replaceText(doc, nextContent, opts = {}) {
|
|
346
|
+
const ytext = getYText(doc);
|
|
347
|
+
const previous = ytext.toString();
|
|
348
|
+
const previousHash = collabHash(previous);
|
|
349
|
+
const baseHash = opts.baseHash;
|
|
350
|
+
if (!opts.force) {
|
|
351
|
+
if (!baseHash) {
|
|
352
|
+
throw new CollabBaseHashRequiredError(previousHash);
|
|
353
|
+
}
|
|
354
|
+
if (previousHash !== baseHash) {
|
|
355
|
+
throw new CollabConflictError(previousHash, baseHash);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
doc.transact(() => {
|
|
359
|
+
ytext.delete(0, ytext.length);
|
|
360
|
+
if (nextContent) ytext.insert(0, nextContent);
|
|
361
|
+
}, opts.origin ?? "docz-cli");
|
|
362
|
+
return { previousHash, hash: collabHash(nextContent) };
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/collab/types.ts
|
|
366
|
+
var CollabUnknownError = class extends Error {
|
|
367
|
+
constructor(message) {
|
|
368
|
+
super(message);
|
|
369
|
+
this.name = "CollabUnknownError";
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
var CollabPublishError = class extends Error {
|
|
373
|
+
constructor(message, code) {
|
|
374
|
+
super(message);
|
|
375
|
+
this.code = code;
|
|
376
|
+
this.name = "CollabPublishError";
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
// src/collab/room.ts
|
|
381
|
+
import { randomUUID } from "crypto";
|
|
382
|
+
import {
|
|
383
|
+
HocuspocusProvider,
|
|
384
|
+
HocuspocusProviderWebsocket
|
|
385
|
+
} from "@hocuspocus/provider";
|
|
386
|
+
import WebSocket from "ws";
|
|
387
|
+
import * as Y from "yjs";
|
|
388
|
+
|
|
389
|
+
// src/collab/roomName.ts
|
|
390
|
+
function decodePath(path) {
|
|
391
|
+
try {
|
|
392
|
+
return decodeURIComponent(path);
|
|
393
|
+
} catch {
|
|
394
|
+
return path;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
function normalizeCollabFilePath(path) {
|
|
398
|
+
const decoded = decodePath(path.trim()).replace(/\\/g, "/");
|
|
399
|
+
const parts = [];
|
|
400
|
+
for (const part of decoded.split("/")) {
|
|
401
|
+
if (!part || part === ".") continue;
|
|
402
|
+
if (part === "..") throw new Error("invalid collab file path");
|
|
403
|
+
parts.push(part);
|
|
404
|
+
}
|
|
405
|
+
return parts.join("/");
|
|
406
|
+
}
|
|
407
|
+
function buildCollabDocumentName(spaceId, filePath) {
|
|
408
|
+
const sid = spaceId.trim();
|
|
409
|
+
const normalizedPath = normalizeCollabFilePath(filePath);
|
|
410
|
+
if (!sid || !normalizedPath)
|
|
411
|
+
throw new Error("space id and file path are required");
|
|
412
|
+
return `${sid}:${normalizedPath}`;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// src/collab/room.ts
|
|
416
|
+
function wsUrl(baseUrl) {
|
|
417
|
+
const u = new URL(baseUrl);
|
|
418
|
+
u.protocol = u.protocol === "https:" ? "wss:" : "ws:";
|
|
419
|
+
u.pathname = "/collab";
|
|
420
|
+
u.search = "";
|
|
421
|
+
u.hash = "";
|
|
422
|
+
return u.toString();
|
|
423
|
+
}
|
|
424
|
+
function withTimeout(promise, ms, label) {
|
|
425
|
+
let timer;
|
|
426
|
+
const timeout = new Promise((_, reject) => {
|
|
427
|
+
timer = setTimeout(
|
|
428
|
+
() => reject(new CollabUnknownError(`${label} timed out after ${ms}ms`)),
|
|
429
|
+
ms
|
|
430
|
+
);
|
|
431
|
+
});
|
|
432
|
+
return Promise.race([promise, timeout]).finally(() => {
|
|
433
|
+
if (timer) clearTimeout(timer);
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
var CollabRoomClient = class {
|
|
437
|
+
doc = new Y.Doc();
|
|
438
|
+
provider = null;
|
|
439
|
+
websocketProvider = null;
|
|
440
|
+
openOptions = null;
|
|
441
|
+
pending = /* @__PURE__ */ new Map();
|
|
442
|
+
connected = false;
|
|
443
|
+
synced = false;
|
|
444
|
+
readOnly = false;
|
|
445
|
+
async open(options) {
|
|
446
|
+
const openOptions = {
|
|
447
|
+
...options,
|
|
448
|
+
path: normalizeCollabFilePath(options.path),
|
|
449
|
+
timeoutMs: options.timeoutMs ?? 3e4
|
|
450
|
+
};
|
|
451
|
+
this.openOptions = openOptions;
|
|
452
|
+
const opened = new Promise((resolve, reject) => {
|
|
453
|
+
const websocketProvider = new HocuspocusProviderWebsocket({
|
|
454
|
+
url: wsUrl(openOptions.baseUrl),
|
|
455
|
+
WebSocketPolyfill: WebSocket,
|
|
456
|
+
parameters: {
|
|
457
|
+
space_id: openOptions.spaceId,
|
|
458
|
+
file_path: openOptions.path,
|
|
459
|
+
client: openOptions.client,
|
|
460
|
+
client_version: openOptions.clientVersion
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
this.websocketProvider = websocketProvider;
|
|
464
|
+
const provider = new HocuspocusProvider({
|
|
465
|
+
websocketProvider,
|
|
466
|
+
name: buildCollabDocumentName(openOptions.spaceId, openOptions.path),
|
|
467
|
+
document: this.doc,
|
|
468
|
+
token: () => openOptions.token,
|
|
469
|
+
onAuthenticated: () => {
|
|
470
|
+
},
|
|
471
|
+
onAuthenticationFailed: (data) => {
|
|
472
|
+
reject(
|
|
473
|
+
new CollabPublishError(
|
|
474
|
+
`collab authentication failed: ${data.reason}`,
|
|
475
|
+
"auth_failed"
|
|
476
|
+
)
|
|
477
|
+
);
|
|
478
|
+
},
|
|
479
|
+
onStatus: ({ status }) => {
|
|
480
|
+
this.connected = status === "connected";
|
|
481
|
+
},
|
|
482
|
+
onSynced: ({ state }) => {
|
|
483
|
+
this.synced = state;
|
|
484
|
+
if (state) resolve();
|
|
485
|
+
},
|
|
486
|
+
onStateless: ({ payload }) => {
|
|
487
|
+
this.handleStateless(payload);
|
|
488
|
+
},
|
|
489
|
+
onDisconnect: () => {
|
|
490
|
+
this.connected = false;
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
this.provider = provider;
|
|
494
|
+
});
|
|
495
|
+
await withTimeout(opened, openOptions.timeoutMs, "collab open");
|
|
496
|
+
return this.read();
|
|
497
|
+
}
|
|
498
|
+
read() {
|
|
499
|
+
if (!this.openOptions) throw new Error("collab room is not open");
|
|
500
|
+
const content = readText(this.doc);
|
|
501
|
+
return {
|
|
502
|
+
spaceId: this.openOptions.spaceId,
|
|
503
|
+
path: this.openOptions.path,
|
|
504
|
+
content,
|
|
505
|
+
collabHash: collabHash(content),
|
|
506
|
+
connected: this.connected && this.synced,
|
|
507
|
+
readOnly: this.readOnly
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
write(content, opts = {}) {
|
|
511
|
+
if (!this.openOptions) throw new Error("collab room is not open");
|
|
512
|
+
if (this.readOnly)
|
|
513
|
+
throw new CollabPublishError("collab room is read-only", "read_only");
|
|
514
|
+
const result = replaceText(this.doc, content, {
|
|
515
|
+
baseHash: opts.baseHash,
|
|
516
|
+
force: opts.force,
|
|
517
|
+
origin: this.openOptions.client
|
|
518
|
+
});
|
|
519
|
+
return {
|
|
520
|
+
spaceId: this.openOptions.spaceId,
|
|
521
|
+
path: this.openOptions.path,
|
|
522
|
+
previousHash: result.previousHash,
|
|
523
|
+
collabHash: result.hash
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
async publish(timeoutMs) {
|
|
527
|
+
if (!this.provider || !this.openOptions)
|
|
528
|
+
throw new Error("collab room is not open");
|
|
529
|
+
const reqId = randomUUID();
|
|
530
|
+
const ms = timeoutMs ?? this.openOptions.timeoutMs ?? 3e4;
|
|
531
|
+
const result = new Promise((resolve, reject) => {
|
|
532
|
+
const timer = setTimeout(() => {
|
|
533
|
+
this.pending.delete(reqId);
|
|
534
|
+
reject(
|
|
535
|
+
new CollabUnknownError(`collab publish ack timed out after ${ms}ms`)
|
|
536
|
+
);
|
|
537
|
+
}, ms);
|
|
538
|
+
this.pending.set(reqId, { resolve, reject, timer });
|
|
539
|
+
});
|
|
540
|
+
this.provider.sendStateless(
|
|
541
|
+
JSON.stringify({
|
|
542
|
+
type: "publish",
|
|
543
|
+
reqId,
|
|
544
|
+
client: this.openOptions.client,
|
|
545
|
+
client_version: this.openOptions.clientVersion
|
|
546
|
+
})
|
|
547
|
+
);
|
|
548
|
+
return result;
|
|
549
|
+
}
|
|
550
|
+
close() {
|
|
551
|
+
for (const [reqId, pending] of this.pending) {
|
|
552
|
+
clearTimeout(pending.timer);
|
|
553
|
+
pending.reject(
|
|
554
|
+
new CollabUnknownError("collab room closed before publish ack")
|
|
555
|
+
);
|
|
556
|
+
this.pending.delete(reqId);
|
|
557
|
+
}
|
|
558
|
+
this.provider?.destroy();
|
|
559
|
+
this.websocketProvider?.destroy();
|
|
560
|
+
this.provider = null;
|
|
561
|
+
this.websocketProvider = null;
|
|
562
|
+
this.doc.destroy();
|
|
563
|
+
}
|
|
564
|
+
handleStateless(payload) {
|
|
565
|
+
let msg;
|
|
566
|
+
try {
|
|
567
|
+
msg = JSON.parse(payload);
|
|
568
|
+
} catch {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (!msg.reqId) return;
|
|
572
|
+
const pending = this.pending.get(msg.reqId);
|
|
573
|
+
if (!pending) return;
|
|
574
|
+
this.pending.delete(msg.reqId);
|
|
575
|
+
clearTimeout(pending.timer);
|
|
576
|
+
if (msg.type === "publish_ack" || msg.type === "recreate_after_delete_ack") {
|
|
577
|
+
if (!this.openOptions) {
|
|
578
|
+
pending.reject(
|
|
579
|
+
new CollabUnknownError("collab room closed before publish ack")
|
|
580
|
+
);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
pending.resolve({
|
|
584
|
+
spaceId: this.openOptions.spaceId,
|
|
585
|
+
path: this.openOptions.path,
|
|
586
|
+
ref: msg.ref || "",
|
|
587
|
+
contentRef: msg.content_ref || "",
|
|
588
|
+
externalBackup: msg.external_backup || ""
|
|
589
|
+
});
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
pending.reject(
|
|
593
|
+
new CollabPublishError(
|
|
594
|
+
`collab publish failed${msg.code ? `: ${msg.code}` : ""}`,
|
|
595
|
+
msg.code
|
|
596
|
+
)
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
async function withCollabRoom(options, fn) {
|
|
601
|
+
const room = new CollabRoomClient();
|
|
602
|
+
try {
|
|
603
|
+
await room.open(options);
|
|
604
|
+
return await fn(room);
|
|
605
|
+
} finally {
|
|
606
|
+
room.close();
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
315
610
|
// src/config.ts
|
|
316
611
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
317
612
|
import { homedir } from "os";
|
|
@@ -347,6 +642,96 @@ function getConfigPath() {
|
|
|
347
642
|
// src/commands.ts
|
|
348
643
|
import { existsSync as existsSync2, readFileSync as readFileSync2, statSync } from "fs";
|
|
349
644
|
import { basename } from "path";
|
|
645
|
+
|
|
646
|
+
// src/collab/bridge.ts
|
|
647
|
+
import readline from "readline";
|
|
648
|
+
function send(value) {
|
|
649
|
+
process.stdout.write(`${JSON.stringify(value)}
|
|
650
|
+
`);
|
|
651
|
+
}
|
|
652
|
+
async function startCollabBridge(openRoom) {
|
|
653
|
+
let room = null;
|
|
654
|
+
let cleanupObserver = null;
|
|
655
|
+
const rl = readline.createInterface({
|
|
656
|
+
input: process.stdin,
|
|
657
|
+
crlfDelay: Number.POSITIVE_INFINITY
|
|
658
|
+
});
|
|
659
|
+
function attachObserver(nextRoom) {
|
|
660
|
+
cleanupObserver?.();
|
|
661
|
+
const ytext = nextRoom.doc.getText("content");
|
|
662
|
+
const observer = () => {
|
|
663
|
+
const content = ytext.toString();
|
|
664
|
+
send({ event: "document_change", content, hash: collabHash(content) });
|
|
665
|
+
};
|
|
666
|
+
ytext.observe(observer);
|
|
667
|
+
cleanupObserver = () => ytext.unobserve(observer);
|
|
668
|
+
}
|
|
669
|
+
function closeRoom() {
|
|
670
|
+
cleanupObserver?.();
|
|
671
|
+
cleanupObserver = null;
|
|
672
|
+
room?.close();
|
|
673
|
+
room = null;
|
|
674
|
+
}
|
|
675
|
+
for await (const line of rl) {
|
|
676
|
+
if (!line.trim()) continue;
|
|
677
|
+
let req;
|
|
678
|
+
try {
|
|
679
|
+
req = JSON.parse(line);
|
|
680
|
+
} catch (err) {
|
|
681
|
+
send({ event: "error", code: "bad_json", message: String(err) });
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
try {
|
|
685
|
+
if (req.method === "open") {
|
|
686
|
+
const target = String(req.params?.target ?? "");
|
|
687
|
+
closeRoom();
|
|
688
|
+
room = await openRoom(target);
|
|
689
|
+
attachObserver(room);
|
|
690
|
+
const current = room.read();
|
|
691
|
+
send({
|
|
692
|
+
id: req.id,
|
|
693
|
+
result: {
|
|
694
|
+
content: current.content,
|
|
695
|
+
hash: current.collabHash,
|
|
696
|
+
read_only: current.readOnly
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
send({
|
|
700
|
+
event: "opened",
|
|
701
|
+
content: current.content,
|
|
702
|
+
hash: current.collabHash
|
|
703
|
+
});
|
|
704
|
+
} else if (req.method === "local_change") {
|
|
705
|
+
if (!room) throw new Error("room is not open");
|
|
706
|
+
const content = String(req.params?.content ?? "");
|
|
707
|
+
const baseHash = req.params?.base_hash ? String(req.params.base_hash) : void 0;
|
|
708
|
+
const result = room.write(content, {
|
|
709
|
+
baseHash,
|
|
710
|
+
force: req.params?.force === true
|
|
711
|
+
});
|
|
712
|
+
send({ id: req.id, result: { hash: result.collabHash } });
|
|
713
|
+
} else if (req.method === "publish") {
|
|
714
|
+
if (!room) throw new Error("room is not open");
|
|
715
|
+
send({ id: req.id, result: await room.publish() });
|
|
716
|
+
} else if (req.method === "status") {
|
|
717
|
+
send({ id: req.id, result: room ? room.read() : { connected: false } });
|
|
718
|
+
} else if (req.method === "close") {
|
|
719
|
+
closeRoom();
|
|
720
|
+
send({ id: req.id, result: { ok: true } });
|
|
721
|
+
} else {
|
|
722
|
+
throw new Error(`unknown method: ${req.method}`);
|
|
723
|
+
}
|
|
724
|
+
} catch (err) {
|
|
725
|
+
send({
|
|
726
|
+
id: req.id,
|
|
727
|
+
error: err instanceof Error ? err.message : String(err)
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
closeRoom();
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// src/commands.ts
|
|
350
735
|
function getClient() {
|
|
351
736
|
const token = getToken();
|
|
352
737
|
if (!token) {
|
|
@@ -357,6 +742,32 @@ function getClient() {
|
|
|
357
742
|
}
|
|
358
743
|
return new DocSyncClient(getBaseUrl(), token);
|
|
359
744
|
}
|
|
745
|
+
function getRequiredToken() {
|
|
746
|
+
const token = getToken();
|
|
747
|
+
if (!token) {
|
|
748
|
+
console.error(
|
|
749
|
+
"Error: No token configured.\nRun `docz login` or set DOCSYNC_API_TOKEN environment variable."
|
|
750
|
+
);
|
|
751
|
+
process.exit(1);
|
|
752
|
+
}
|
|
753
|
+
return token;
|
|
754
|
+
}
|
|
755
|
+
async function buildCollabOpenOptions(target, opts = {}) {
|
|
756
|
+
const api = getClient();
|
|
757
|
+
const { spaceId, path } = await resolveTarget(api, [target]);
|
|
758
|
+
if (!path) {
|
|
759
|
+
throw new Error("file path is required for collaborative editing");
|
|
760
|
+
}
|
|
761
|
+
return {
|
|
762
|
+
baseUrl: getBaseUrl(),
|
|
763
|
+
token: getRequiredToken(),
|
|
764
|
+
spaceId,
|
|
765
|
+
path,
|
|
766
|
+
client: opts.client ?? "docz-cli",
|
|
767
|
+
clientVersion: opts.clientVersion ?? "0.10.0",
|
|
768
|
+
timeoutMs: opts.timeout
|
|
769
|
+
};
|
|
770
|
+
}
|
|
360
771
|
function parseTarget(args) {
|
|
361
772
|
if (args.length === 0) {
|
|
362
773
|
console.error(
|
|
@@ -887,6 +1298,124 @@ function registerCommands(program) {
|
|
|
887
1298
|
const ref = await client.getFileRef(spaceId, path);
|
|
888
1299
|
console.log(ref.url);
|
|
889
1300
|
});
|
|
1301
|
+
const collab = program.command("collab").description("Collaborative editing via Docz realtime rooms");
|
|
1302
|
+
collab.command("cat").description(
|
|
1303
|
+
"Read collaborative document \u2014 docz collab cat <space>:<path> or <url>"
|
|
1304
|
+
).argument("<target>", "space:path or short URL").option("--raw", "Output raw content only").option("--timeout <ms>", "Open timeout in milliseconds", Number).action(
|
|
1305
|
+
async (target, opts) => {
|
|
1306
|
+
const open = await buildCollabOpenOptions(target, {
|
|
1307
|
+
timeout: opts.timeout
|
|
1308
|
+
});
|
|
1309
|
+
await withCollabRoom(open, async (room) => {
|
|
1310
|
+
const result = room.read();
|
|
1311
|
+
if (!opts.raw) {
|
|
1312
|
+
console.error(`collab_hash: ${result.collabHash}`);
|
|
1313
|
+
console.error(`read_only: ${result.readOnly ? "true" : "false"}`);
|
|
1314
|
+
console.error("---");
|
|
1315
|
+
}
|
|
1316
|
+
process.stdout.write(result.content);
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
);
|
|
1320
|
+
collab.command("write").description(
|
|
1321
|
+
"Write collaborative document \u2014 docz collab write <space>:<path> <content>"
|
|
1322
|
+
).argument("<target>", "space:path or short URL").argument("<content>", "File content (or - for stdin)").option("--base-collab-hash <hash>", "Hash returned by docz collab cat").option("--force", "Skip collaborative hash conflict detection").option("--no-publish", "Only update realtime room, do not flush to repo").option("--timeout <ms>", "Open/publish timeout in milliseconds", Number).action(
|
|
1323
|
+
async (target, content, opts) => {
|
|
1324
|
+
const open = await buildCollabOpenOptions(target, {
|
|
1325
|
+
timeout: opts.timeout
|
|
1326
|
+
});
|
|
1327
|
+
const body = content === "-" ? await readStdin() : content;
|
|
1328
|
+
if (Buffer.byteLength(body, "utf-8") > MAX_SAVE_SIZE) {
|
|
1329
|
+
console.error(
|
|
1330
|
+
"Error: content exceeds 2MB limit. Use `docz upload` for large files."
|
|
1331
|
+
);
|
|
1332
|
+
process.exit(1);
|
|
1333
|
+
}
|
|
1334
|
+
try {
|
|
1335
|
+
await withCollabRoom(open, async (room) => {
|
|
1336
|
+
const write = room.write(body, {
|
|
1337
|
+
baseHash: opts.baseCollabHash,
|
|
1338
|
+
force: opts.force
|
|
1339
|
+
});
|
|
1340
|
+
if (opts.publish === false) {
|
|
1341
|
+
console.log(
|
|
1342
|
+
`Updated collaborative room (collab_hash: ${write.collabHash})`
|
|
1343
|
+
);
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
const published = await room.publish(opts.timeout);
|
|
1347
|
+
console.log(
|
|
1348
|
+
`Published: ${published.path} (ref: ${published.ref}, collab_hash: ${write.collabHash})`
|
|
1349
|
+
);
|
|
1350
|
+
if (published.externalBackup) {
|
|
1351
|
+
console.log(`External backup: ${published.externalBackup}`);
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1354
|
+
} catch (err) {
|
|
1355
|
+
if (err instanceof CollabConflictError) {
|
|
1356
|
+
console.error(
|
|
1357
|
+
`Error: collaborative document changed. Re-read and retry. current=${err.currentHash} base=${err.baseHash}`
|
|
1358
|
+
);
|
|
1359
|
+
process.exit(1);
|
|
1360
|
+
}
|
|
1361
|
+
if (err instanceof CollabBaseHashRequiredError) {
|
|
1362
|
+
console.error(
|
|
1363
|
+
`Error: --base-collab-hash is required unless --force is set. Re-read first to get the latest hash. current=${err.currentHash}`
|
|
1364
|
+
);
|
|
1365
|
+
process.exit(1);
|
|
1366
|
+
}
|
|
1367
|
+
if (err instanceof CollabUnknownError) {
|
|
1368
|
+
console.error(
|
|
1369
|
+
`Unknown state: ${err.message}. The server may have processed the publish; please re-read before retrying.`
|
|
1370
|
+
);
|
|
1371
|
+
process.exit(75);
|
|
1372
|
+
}
|
|
1373
|
+
throw err;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
);
|
|
1377
|
+
collab.command("publish").description(
|
|
1378
|
+
"Flush collaborative document to repo \u2014 docz collab publish <space>:<path>"
|
|
1379
|
+
).argument("<target>", "space:path or short URL").option("--timeout <ms>", "Open/publish timeout in milliseconds", Number).action(async (target, opts) => {
|
|
1380
|
+
const open = await buildCollabOpenOptions(target, {
|
|
1381
|
+
timeout: opts.timeout
|
|
1382
|
+
});
|
|
1383
|
+
try {
|
|
1384
|
+
await withCollabRoom(open, async (room) => {
|
|
1385
|
+
const result = await room.publish(opts.timeout);
|
|
1386
|
+
console.log(`Published: ${result.path} (ref: ${result.ref})`);
|
|
1387
|
+
if (result.externalBackup) {
|
|
1388
|
+
console.log(`External backup: ${result.externalBackup}`);
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
} catch (err) {
|
|
1392
|
+
if (err instanceof CollabUnknownError) {
|
|
1393
|
+
console.error(
|
|
1394
|
+
`Unknown state: ${err.message}. The server may have processed the publish; please re-read before retrying.`
|
|
1395
|
+
);
|
|
1396
|
+
process.exit(75);
|
|
1397
|
+
}
|
|
1398
|
+
throw err;
|
|
1399
|
+
}
|
|
1400
|
+
});
|
|
1401
|
+
collab.command("bridge").description("Start local JSONL bridge for terminal editors").option("--client <name>", "Client name sent to Docz", "docz.nvim").option(
|
|
1402
|
+
"--client-version <version>",
|
|
1403
|
+
"Client version sent to Docz",
|
|
1404
|
+
"0.10.0"
|
|
1405
|
+
).option("--timeout <ms>", "Open/publish timeout in milliseconds", Number).action(
|
|
1406
|
+
async (opts) => {
|
|
1407
|
+
await startCollabBridge(async (target) => {
|
|
1408
|
+
const open = await buildCollabOpenOptions(target, {
|
|
1409
|
+
client: opts.client,
|
|
1410
|
+
clientVersion: opts.clientVersion,
|
|
1411
|
+
timeout: opts.timeout
|
|
1412
|
+
});
|
|
1413
|
+
const room = new CollabRoomClient();
|
|
1414
|
+
await room.open(open);
|
|
1415
|
+
return room;
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
);
|
|
890
1419
|
program.command("diff").description(
|
|
891
1420
|
"Show changes \u2014 docz diff <space>[:<path>] <commit> [<from>] or <url> <commit> [<from>]"
|
|
892
1421
|
).argument("<target>", "space or space:path or short URL").argument("<to>", "Commit hash").argument("[from]", "From commit hash (default: to^)").action(async (target, to, from) => {
|
|
@@ -915,6 +1444,10 @@ function registerCommands(program) {
|
|
|
915
1444
|
export {
|
|
916
1445
|
ConflictError,
|
|
917
1446
|
DocSyncClient,
|
|
1447
|
+
CollabConflictError,
|
|
1448
|
+
CollabBaseHashRequiredError,
|
|
1449
|
+
CollabUnknownError,
|
|
1450
|
+
withCollabRoom,
|
|
918
1451
|
getBaseUrl,
|
|
919
1452
|
getToken,
|
|
920
1453
|
parseExpires,
|
|
@@ -922,4 +1455,4 @@ export {
|
|
|
922
1455
|
markdownImageRef,
|
|
923
1456
|
registerCommands
|
|
924
1457
|
};
|
|
925
|
-
//# sourceMappingURL=chunk-
|
|
1458
|
+
//# sourceMappingURL=chunk-26LVQHYL.js.map
|